﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VNet.VisiSuite.Dal.DatabaseSpecific;
using VNet.VisiSuite.Dal.EntityClasses;
using VNet.VisiSuite.Dal.FactoryClasses;
using VNet.VisiSuite.Dal.HelperClasses;
using VNet.VisiSuite.Service.Contact.Message.Base;
using SD.LLBLGen.Pro.ORMSupportClasses;
using SD.LLBLGen.Pro.QuerySpec;
using SD.LLBLGen.Pro.QuerySpec.Adapter;
using VNet.VisiSuite.Common.Logger;
using VNet.VisiSuite.Dal;

namespace VNet.VisiSuite.BusinessLogic
{
#if DEBUG
    /// <summary>
    /// This class is used for the sole purpose of generating test data for visi central application. 
    /// It should be removed (including all references to it) on final build.
    /// </summary>
    public class VisiCentralTestDataUtil
    {
        private QueryFactory _queryFactory = new QueryFactory();

        public GetDataResponse<bool> CreateDataTest(string cellName)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    // Fetch cell 001 from database
                    var cellQuery = _queryFactory.Cell.Where(CellFields.CellName == cellName);
                    var cellEntity = adapter.FetchFirst(cellQuery);

                    DeleteInitialData(cellEntity);
                    CreateInitialData(cellEntity);
                    return new GetDataResponse<bool>() { Data = true, Success = true };
                }
                catch(Exception ex)
                {
                    FileLog.Error("CreateDataTest", ex);
                    return new GetDataResponse<bool>() { Data = false, Success = false, Error = new ServiceError(ex.Message, ex.StackTrace) };
                }
            }
        }

        public GetDataResponse<bool> CreateProductionHistoryAtInterval(string cellName)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    // Fetch cell 001 from database
                    var cellQuery = _queryFactory.Cell.Where(CellFields.CellName == cellName);
                    var cellEntity = adapter.FetchFirst(cellQuery);

                    if (cellEntity != null)
                    {
                        // Find current cell shift entity
                        var areaShiftEntity = LookupCurrentAreaShift(cellEntity);
                        if (areaShiftEntity != null)
                        {
                            var cellShiftQuery = _queryFactory.CellShift.Where(CellShiftFields.CellId == cellEntity.CellId);
                            cellShiftQuery = cellShiftQuery.Where(CellShiftFields.AreaShiftId == areaShiftEntity.AreaShiftId);
                            var cellShiftEntity = adapter.FetchFirst(cellShiftQuery);
                            if (cellShiftEntity != null)
                            {
                                // Create random production history item
                                var rcec = new EntityCollection<ReasonCodeEntity>();
                                var qf = new QueryFactory();
                                var qry2 = qf.ReasonCode
                                    .Where(ReasonCodeFields.AreaId == areaShiftEntity.AreaId);
                                adapter.FetchQuery(qry2, rcec);

                                var rand = new Random();
                                int ii;
                                var now = DateTime.Now;

                                ii = rand.Next(0, rcec.Count - 1);
                                AddProductionHistory(cellEntity, cellShiftEntity.CellShiftId, rcec[ii].ReasonCodeId, null, DateTime.Now, 0, rcec[ii].ReasonCodeId == 518, false);

                                return new GetDataResponse<bool>() { Data = true, Success = true };
                            }
                        }
                    }
                }
                catch(Exception ex)
                {
                    FileLog.Error("CreateProductionHistoryAtInterval", ex);
                    return new GetDataResponse<bool>() { Data = false, Success = false, Error = new ServiceError(ex.Message, ex.StackTrace) };
                }
            }

            return new GetDataResponse<bool>() { Data = false, Success = true };
        }

        private void DeleteInitialData(CellEntity cellEntity)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    if (cellEntity != null)
                    {
                        // Delete all cell shift and production history for cell '001'
                        var cellShiftQuery = _queryFactory.CellShift.Where(CellShiftFields.CellId == cellEntity.CellId);
                        var cellShiftList = adapter.FetchQuery(cellShiftQuery);
                        foreach (CellShiftEntity cellShift in cellShiftList)
                        {
                            var productionHistoryQuery = _queryFactory.ProductionHistory.Where(ProductionHistoryFields.CellShiftId == cellShift.CellShiftId);
                            var productionHistoryList = adapter.FetchQuery(productionHistoryQuery);

                            foreach (ProductionHistoryEntity productionHistory in productionHistoryList)
                            {
                                adapter.DeleteEntity(productionHistory);
                            }
                            adapter.DeleteEntity(cellShift);
                        }
                    }
                }
                catch(Exception ex)
                {
                    FileLog.Error("DeleteInitialData", ex);
                }
            }
        }

        private void CreateInitialData(CellEntity cellEntity)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    if (cellEntity != null)
                    {
                        var areaShiftEntity = LookupCurrentAreaShift(cellEntity);

                        if (areaShiftEntity != null)
                        {
                            // Create cell shift entity for cell entity and area shift entity
                            var cellShiftEntity = new CellShiftEntity();
                            cellShiftEntity.CellId = cellEntity.CellId;
                            cellShiftEntity.AreaShiftId = areaShiftEntity.AreaShiftId;
                            cellShiftEntity.StartTime = DateTime.Now.Date.Add(areaShiftEntity.StartTime);

                            // If area shift has end time < start time, that is a night shift, then add 1 day to the end time.
                            if (areaShiftEntity.EndTime < areaShiftEntity.StartTime)
                            {
                                cellShiftEntity.EndTime = DateTime.Now.Date.AddDays(1).Add(areaShiftEntity.EndTime);
                            }
                            else
                                cellShiftEntity.EndTime = DateTime.Now.Date.Add(areaShiftEntity.EndTime);
                            cellShiftEntity.ClientId = 1;

                            adapter.SaveEntity(cellShiftEntity, true);

                            // Create random production history item from start of the shift to current time
                            var rcec = new EntityCollection<ReasonCodeEntity>();
                            var qf = new QueryFactory();
                            var qry2 = qf.ReasonCode
                                .Where(ReasonCodeFields.AreaId == areaShiftEntity.AreaId);
                            adapter.FetchQuery(qry2, rcec);

                            var rand = new Random();
                            int ii;
                            var now = DateTime.Now;

                            var shiftStart = cellShiftEntity.StartTime;

                            while (shiftStart < now)
                            {
                                ii = rand.Next(0, rcec.Count - 1);
                                AddProductionHistory(cellEntity, cellShiftEntity.CellShiftId, rcec[ii].ReasonCodeId, null, shiftStart, 0, rcec[ii].ReasonCodeId == 518, false);
                                ii = rand.Next(1, 6);
                                shiftStart = shiftStart.AddMinutes(ii);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    FileLog.Error("CreateInitialData", ex);
                }
            }
        }

        private void AddProductionHistory(CellEntity cellEntity, int cellShiftId, int reasonId, int? productionPlanId, DateTime start, int? key, bool trackDowntimeAck, bool offline)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    // Note reason_id == 0 means "good"; database entry should be NULL
                    // Save new Entity to database, assign to id
                    ProductionHistoryEntity entity = new ProductionHistoryEntity();
                    entity.CellShiftId = cellShiftId;
                    entity.WorkContent = null;
                    entity.Operators = 1;

                    if (reasonId > 0)
                    {
                        entity.ReasonCodeId = reasonId;
                    }
                    else
                    {
                        entity.ReasonCodeId = null;
                    }

                    if (productionPlanId.HasValue && productionPlanId > 0)
                        entity.ProductionPlanId = productionPlanId;
                    else
                        entity.ProductionPlanId = null;

                    if (!trackDowntimeAck)
                        entity.DowntimeAckTime = start; // zero time waiting for operator
                    entity.StartTime = start;
                    entity.EndTime = null;
                    entity.VisiBoxKey = key;

                    // Find last production entity with null end time
                    var prevEntityQuery = _queryFactory.ProductionHistory.Where(ProductionHistoryFields.EndTime == DBNull.Value);
                    prevEntityQuery = prevEntityQuery.Where(ProductionHistoryFields.CellShiftId == entity.CellShiftId);
                    var prevEntity = adapter.FetchFirst(prevEntityQuery);

                    if (prevEntity != null)
                        UpdateProductionHistoryEndTime(prevEntity.ProductionHistoryId, entity.StartTime);

                    // Update Cell Status
                    UpdateCellStatus(cellEntity, prevEntity, offline);

                    adapter.SaveEntity(entity);
                }
                catch (Exception ex)
                {
                    FileLog.Error("AddProductionHistory() - ", ex);
                }
            }
        }

        private void UpdateProductionHistoryEndTime(int production_history_id, DateTime end)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    // NOTE IF THERE IS A VALID ReasonCodeID for this item and DowntimeAckTime is null, then we need to set DowntimeAckTime = EndTime
                    DateTime start;

                    ProductionHistoryEntity entity = new ProductionHistoryEntity(production_history_id);
                    IPrefetchPath2 prefetch = new PrefetchPath2(EntityType.ProductionHistoryEntity);
                    prefetch.Add(ProductionHistoryEntity.PrefetchPathReasonCode);
                    adapter.FetchEntity(entity, prefetch);
                    if (!entity.DowntimeAckTime.HasValue || entity.DowntimeAckTime > end)
                    {
                        entity.DowntimeAckTime = end;
                    }

                    start = entity.StartTime;
                    entity.EndTime = end;
                    adapter.SaveEntity(entity);

                }
                catch (Exception ex)
                {
                    FileLog.Error("updateProductionHistoryEndTime() - ", ex);
                }
            }
        }

        private AreaShiftEntity LookupCurrentAreaShift(CellEntity cellEntity)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    if (cellEntity != null)
                    {
                        // Look up area shift item for current time (day)
                        AreaShiftEntity areaShiftEntity;
                        var areaDayShiftQuery = _queryFactory.AreaShift.Where(AreaShiftFields.StartTime <= DateTime.Now.TimeOfDay);
                        areaDayShiftQuery = areaDayShiftQuery.Where(AreaShiftFields.EndTime >= DateTime.Now.TimeOfDay);
                        areaDayShiftQuery = areaDayShiftQuery.Where(AreaShiftFields.EndTime > AreaShiftFields.StartTime);
                        areaShiftEntity = adapter.FetchFirst(areaDayShiftQuery);
                        // If unable to find a day shift, switch to night shift
                        if (areaShiftEntity == null)
                        {
                            // First part when it is still in the day.
                            var areaNightShiftQuery1 = _queryFactory.AreaShift.Where(AreaShiftFields.StartTime <= DateTime.Now.TimeOfDay);
                            areaNightShiftQuery1 = areaNightShiftQuery1.Where(AreaShiftFields.EndTime < AreaShiftFields.StartTime);
                            areaShiftEntity = adapter.FetchFirst(areaNightShiftQuery1);

                            if (areaShiftEntity == null)
                            {
                                // Second part when it is in the next day
                                var areaNightShiftQuery2 = _queryFactory.AreaShift.Where(AreaShiftFields.EndTime >= DateTime.Now.TimeOfDay);
                                areaNightShiftQuery2 = areaNightShiftQuery2.Where(AreaShiftFields.EndTime < AreaShiftFields.StartTime);
                                areaShiftEntity = adapter.FetchFirst(areaNightShiftQuery2);
                            }
                        }

                        return areaShiftEntity;
                    }
                }
                catch(Exception ex)
                {
                    FileLog.Error("LookupCurrentAreaShift", ex);
                }
            }

            return null;
        }

        public void UpdateCellStatus(CellEntity cellEntity, ProductionHistoryEntity productionHistoryEntity, bool isOffline)
        {
            try
            {
                if (cellEntity !=null && productionHistoryEntity != null)
                {
                    using (var adapter = new DataAccessAdapter())
                    {
                        var qf = new QueryFactory();
                        var qry = qf.CellStatus.Where(CellStatusFields.CellId == cellEntity.CellId);
                        CellStatusEntity cell_status = adapter.FetchFirst(qry);
                        if (cell_status == null)
                            cell_status = new CellStatusEntity() { 
                                TimeStamp = new byte[8]
                            };
                        cell_status.CellId = cellEntity.CellId;
                        cell_status.ReasonCodeId = productionHistoryEntity.ReasonCodeId.HasValue ? 
                            productionHistoryEntity.ReasonCodeId.Value : 0;
                        cell_status.EventTime = productionHistoryEntity.StartTime;
                        cell_status.Offline = isOffline;
                        cell_status.Flags = 0;
                        cell_status.Status = 0;
                        cell_status.LastSync = productionHistoryEntity.StartTime;
                        if (!adapter.SaveEntity(cell_status))
                            FileLog.Error("UpdateCellStatus() - error saving cell status.");
                    }
                }
            }
            catch (Exception ex)
            {
                FileLog.Error("UpdateCellStatus() - ", ex);
            }
        }
    }
#endif
}
